package com.googlecode.webduff;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.googlecode.webduff.authentication.WebdavAuthentication;
import com.googlecode.webduff.authentication.provider.Credential;
import com.googlecode.webduff.authentication.provider.WebdavAuthenticationProvider;
import com.googlecode.webduff.exceptions.MethodResponseError;
import com.googlecode.webduff.exceptions.WebdavException;
import com.googlecode.webduff.locking.ResourceLocks;
import com.googlecode.webduff.methods.MethodExecutor;
import com.googlecode.webduff.methods.MethodRegistry;
import com.googlecode.webduff.methods.MethodResponse;
import com.googlecode.webduff.store.WebdavStore;
import com.googlecode.webduff.store.WebdavStoreFactory;

import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class WebDavServletBean extends HttpServlet {

	private static final long serialVersionUID = 4163151788047683589L;
	
	private static org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(WebDavServletBean.class);

    private ResourceLocks resourceLocks;
    
    private WebdavStoreFactory theStoreFactory;
    
    private WebdavAuthenticationProvider theAuthentication;
    
    private WebdavAuthentication theAuthorization;
    
    private WebDuffConfiguration servletConfig;
    
    private MimeTyper theMimeTyper;
    
    private MethodRegistry methodsRegistry;

    private WebDuffSessionManager theSessionManager;
    
    public WebDavServletBean() {
        resourceLocks = new ResourceLocks();
        methodsRegistry = new MethodRegistry();
        theSessionManager = new WebDuffSessionManager();
        try {
            MessageDigest.getInstance("MD5");
        } catch (NoSuchAlgorithmException e) {
            throw new IllegalStateException();
        }
    }

    public void init(WebdavStoreFactory storeFactory, WebdavAuthenticationProvider authentication, WebdavAuthentication authorization, MimeTyper mimeTyper) throws ServletException {
        theStoreFactory = storeFactory;
        theAuthentication = authentication;
        theAuthorization = authorization;
        theMimeTyper = mimeTyper;
    }
    
    /**
     * Handles the special WebDAV methods.
     */
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String methodName = request.getMethod();
        log.debug(methodName + "()");
        try {
    		Credential credential = theAuthentication.getCredential(request, response);
    		theAuthorization.authenticate(credential);
    		WebDuffSession aSession = theSessionManager.getSession(theStoreFactory, credential, request);
    		WebdavStore aStore = aSession.getStore();
    		
            MethodExecutor methodToExecute = methodsRegistry.getMethod(methodName, servletConfig, methodsRegistry, aStore, resourceLocks, theMimeTyper, getServletContext());
    		aStore.begin();
    		
            try {
            	log.info(methodName + ".execute()");
                MethodResponse methodResponse = methodToExecute.execute(request, response);
                aStore.commit();
                log.info(methodName + ".commit()");
                handleResponse(methodResponse, response);
            } catch (IOException e) {
            	response.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
                aStore.rollback();
                log.info(methodName + ".rollback()");
                throw new ServletException(e);
            }
        } catch (MethodResponseError e) {
        	handleResponse(e.getErrorResponse(), response);
        	log.debug("Error from Method = " + methodName, e);
        	response.sendError(e.getErrorCode());
		} catch (WebdavException e) {
            log.error("WebdavException", e);
            throw new ServletException(e);
        }
    }

	private void handleResponse(MethodResponse methodResponse, HttpServletResponse response) throws IOException {
		if(methodResponse == null) {
			return;
		}
		
		methodResponse.handle(response);
	}
    
}